%%%-------------------------------------------------------------------
%%% @author  <>
%%% @copyright (C) 2010, 
%%% @doc
%%%
%%% @end
%%% Created : 10 Oct 2010 by  <>
%%%-------------------------------------------------------------------
-module(sip_transaction).

-behaviour(gen_server).

%% API
-export([start_link/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-define(SERVER, ?MODULE). 

-include("../include/sip.hrl").

-record(sip_transaction_state, {sip_session_pid, retransmit_map}).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link([SIP_Session_PID]) ->
    gen_server:start_link(?MODULE, [SIP_Session_PID], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([SIP_Session_PID]) ->
    {ok, #sip_transaction_state{sip_session_pid = SIP_Session_PID, retransmit_map = dict:new()}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @spec handle_call(Request, From, State) ->
%%                                   {reply, Reply, State} |
%%                                   {reply, Reply, State, Timeout} |
%%                                   {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, Reply, State} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast({create_ti, {Transport_Type, Dest_Server, Dest_Port}, Branch_ID, Method, Request}, State) ->
    #sip_transaction_state{sip_session_pid = SIP_Session_PID, retransmit_map = Retransmit_Map} = State,
    io:format("Inside create_ti function ~n"),
    case catch timer:apply_after(4*?T1, gen_server, cast, [self(), {retransmit, Branch_ID, ?T1*64 - 4*?T1}]) of
        {ok, TRef} ->
            Retransmit_State = #retransmit{method = Method, request = Request, secs = 2*?T1, timer = TRef, dest_server = Dest_Server, dest_port = Dest_Port, transport_type = Transport_Type},
            gen_server:cast(SIP_Session_PID, {send_data, Transport_Type, Dest_Server, Dest_Port, Request}),
            {noreply, State#sip_transaction_state{retransmit_map = dict:store(Branch_ID, Retransmit_State, Retransmit_Map)}};
        _ ->
            gen_server:cast(SIP_Session_PID, {timeout, Branch_ID, Method}),
            {noreply, State}
    end;

handle_cast({retransmit, Branch_ID, Timeout}, State) ->
    #sip_transaction_state{sip_session_pid = SIP_Session_PID, retransmit_map = Retransmit_Map} = State,
    case catch dict:find(Branch_ID, Retransmit_Map) of
        {ok, Retransmit_State} ->
            #retransmit{method = Method, request = Request, secs = Secs, dest_server = Dest_Server, dest_port = Dest_Port, transport_type = Transport_Type} = Retransmit_State,
            if 
                Timeout =< 0 ->
                    gen_server:cast(SIP_Session_PID, {timeout, Branch_ID, Method}),
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}};
                Secs < ?T2 ->
                    case catch timer:apply_after(2*Secs, gen_server, cast, [self(), {retransmit, Branch_ID, Timeout - 2*Secs}]) of
                        {ok, TRef} ->
                            New_Retransmit_State = Retransmit_State#retransmit{secs = 2*Secs, timer = TRef},
                            gen_server:cast(SIP_Session_PID, {send_data, Transport_Type, Dest_Server, Dest_Port, Request}),
                            {noreply, State#sip_transaction_state{retransmit_map = dict:store(Branch_ID, New_Retransmit_State, Retransmit_Map)}};
                        _ ->
                            gen_server:cast(SIP_Session_PID, {timeout, Branch_ID, Method}),
                            {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}}
                    end;
                Secs >= ?T2 ->
                    case catch timer:apply_after(Secs, gen_server, cast, [self(), {retransmit, Branch_ID, Timeout - Secs}]) of
                        {ok, TRef} ->
                            New_Retransmit_State = Retransmit_State#retransmit{secs = Secs, timer = TRef},
                            gen_server:cast(SIP_Session_PID, {send_data, Transport_Type, Dest_Server, Dest_Port, Request}),
                            {noreply, State#sip_transaction_state{retransmit_map = dict:store(Branch_ID, New_Retransmit_State, Retransmit_Map)}};
                        _ ->
                            gen_server:cast(SIP_Session_PID, {timeout, Branch_ID, Method}),
                            {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}}
                    end;
                true ->
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}}
            end;
        _ ->
            {noreply, State}
    end;

handle_cast({got_message, Parse_Record}, State) ->
    io:format("Inside sip_transaction:got_message ~n"),
    #sip_transaction_state{retransmit_map = Retransmit_Map, sip_session_pid = SIP_Session_PID} = State,
    #parse_record{type = Type, branch_id = Branch_ID} = Parse_Record,
    case catch dict:find(Branch_ID, Retransmit_Map) of 
        {ok, Retransmit_State} ->
            #retransmit{timer = TRef} = Retransmit_State,
            catch timer:cancel(TRef),
            if
                Type == status ->
                    gen_server:cast(SIP_Session_PID, {got_response, Parse_Record}),
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}};
                true ->
                    gen_server:cast(SIP_Session_PID, {got_request, Parse_Record}),
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}}
            end;
        _ ->
            if
                Type == status ->
                    gen_server:cast(SIP_Session_PID, {got_response, Parse_Record}),
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}};
                true ->
                    gen_server:cast(SIP_Session_PID, {got_request, Parse_Record}),
                    {noreply, State#sip_transaction_state{retransmit_map = dict:erase(Branch_ID, Retransmit_Map)}}
            end
    end;

handle_cast(kill_process, State) ->
    {stop, normal, State};
        
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    io:format("sip_transaction Terminated ~n"),
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
